<template>
  <a-form
    v-bind="$attrs"
    ref="aform"
    :disabled="disabled"
    :size="size"
    :model="formModel"
    :label-align="(labelAlign as any)"
    class="component-form"
  >
    <a-row style="width: 100%" v-bind="rootRow">
      <template
        v-for="formFiledItem of formFiledListLocal"
        :key="formFiledItem.filed"
      >
        <FormItem
          :style="formItemStyles"
          :form-filed-item="formFiledItem"
          :form-model="formModel"
          :form-filed-list-local="formFiledListLocal"
          :form-filed-list="formFiledList"
          :label-width="labelWidth"
          :show-colon="showColon"
          :form-diabled="disabled"
          @update-filed="updateFiled"
          @reset-filed="resetFiled"
          @validate-field="validateField"
        >
          <template
            v-for="(item, key) in (formFiledItem?.slots as any)"
            :key="key"
            #[item]="val"
          >
            <slot :name="item" v-bind="val"></slot>
          </template>
        </FormItem>
      </template>
    </a-row>
  </a-form>
</template>

<script lang="ts" setup>
  import {
    computed,
    ref,
    watch,
    defineProps,
    defineEmits,
    unref,
    defineExpose,
  } from 'vue';
  import { Form as AForm } from '@arco-design/web-vue';
  import { deepMerge } from '@/utils/index';
  import { isObject, isString, isArray } from '@/utils/is';

  import { formProps } from './props';
  import type { FormFiled } from './props';

  import FormItem from './formItem.vue';

  import './index.less';

  const props = defineProps(formProps);
  const hideLabel = computed(() => props.hideLabel);

  const emit = defineEmits(['update:modelValue', 'change']);

  const formFiledListLocal = ref<FormFiled[]>([]);
  let originFormFiledList: FormFiled[] = [];

  watch(
    () => props.formFiledList,
    (newval, oldVal) => {
      if (newval?.length) {
        if (!oldVal || (oldVal && oldVal.length === 0)) {
          originFormFiledList = JSON.parse(JSON.stringify(newval));
        }
        newval.forEach((item) => {
          if (item.props) {
            if (!item.props.formItem) item.props.formItem = {};

            if (
              !(item.props.formItem as Record<string, any>).hideLabel &&
              (item.props.formItem as Record<string, any>).hideLabel !== false
            ) {
              Object.assign(item.props.formItem, {
                hideLabel: hideLabel.value,
              });
            }
          }
        });
        formFiledListLocal.value = newval;
      } else {
        formFiledListLocal.value = [];
      }
    },
    {
      immediate: true,
    }
  );
  const formModel = ref<Record<string, any>>({});
  watch(
    () => props.modelValue,
    (newVal) => {
      formModel.value = newVal;
    },
    {
      immediate: true,
    }
  );

  watch(
    () => formModel,
    () => {
      emit('update:modelValue', unref(formModel));
      emit('change', unref(formModel));
    },
    {
      deep: true,
    }
  );

  const updateFiled = (data: FormFiled | FormFiled[]) => {
    const updateData: FormFiled[] = [];
    if (isObject(data)) {
      updateData.push(data as FormFiled);
    }
    if (isArray(data)) {
      updateData.push(...(data as FormFiled[]));
    }

    const hasField = updateData.every((item) => item.filed);

    if (!hasField) {
      console.error('updateFiled - 缺少filed字段');
      return;
    }

    updateData.forEach((t) => {
      const idx = formFiledListLocal.value.findIndex(
        (m) => m.filed === t.filed
      );
      if (idx >= 0) {
        formFiledListLocal.value[idx] = deepMerge(
          formFiledListLocal.value[idx],
          t
        );
      }
    });
  };

  const resetFiled = (data: string | string[]) => {
    const updateData: string[] = [];
    if (isString(data)) {
      updateData.push(data as string);
    }
    if (isArray(data)) {
      updateData.push(...data);
    }
    const updateFiledList: FormFiled[] = [];
    updateData.forEach((t) => {
      const obj = originFormFiledList.find((m) => m.filed === t);
      if (obj) {
        updateFiledList.push(obj);
      }
    });

    updateFiled(updateFiledList);
  };

  const aform = ref<any>(null);
  const validateField = (field: FormFiled, callback: any): any => {
    if (!aform.value) return;
    aform.value.validateField(field, callback);
  };

  defineExpose({
    aform,
    updateFiled,
    resetFiled,
  });
</script>
